#include "CogProduction.h"
#include "CogGrammar.h"
#include "CogEvolver.h"
#include <boost/tokenizer.hpp>

// ------------------------------------------------------------------------------------------

void CogProduction::computeSize()
{
	if (!ntSize) // only if not already set
	{
		vector<CogSymbol>::iterator rhs_iter = rhs.begin();
		for (; rhs_iter != rhs.end(); ++rhs_iter)
		{
			if (!(rhs_iter->flag & COGSF_TERMINAL))
			{
				rhs_iter->pRule->computeSize();
				ntSize += rhs_iter->pRule->ntSize;
			}
		}
		ntSize++;
	}
}

void CogProduction::use() 
{ 
	usedBy++; 
	lhs.pPool->use();
}

void CogProduction::use(size_t times) 
{ 
	usedBy += times; 
	lhs.pPool->use(times); 
}

void CogProduction::unuse()
{
	if (usedBy)
	{
		if (!(--usedBy))
		{
			vector<CogSymbol>::iterator rhs_iter = rhs.begin();
			for (; rhs_iter != rhs.end(); ++rhs_iter)
			{	
				if (!(rhs_iter->flag & COGSF_TERMINAL))
				{
					rhs_iter->pRule->unuse();
				}
			}
		}
		lhs.pPool->unuse();
	}
}

// ----- from/to stream parsing -----

bool CogProduction::fromStream(istream &in, CogGrammar* pGram)
{
	// retrieve a production from the input stream
	string ruleString;
	getline(in, ruleString, '\n');
	// separate-discard on stop, comma, space, tab, carriage return, but separate-keep colon and hat
	boost::char_separator<char> sep("\r", "<>|%@* ,\t\\");
    boost::tokenizer<boost::char_separator<char> > tokens(ruleString, sep);
	boost::tokenizer<boost::char_separator<char> >::iterator tok_iter = tokens.begin();
	bool processingNow = true;
	bool onLHS = true;
	size_t RHScount = 0;
	size_t symbolCount = 0;
	size_t terminalCount = 0;
	bool ntSymbol = false;
	// loop through remaining tokens
	do
	{
		// continue only if there are further tokens
		if (tok_iter == tokens.end())
		{
			if (!symbolCount)
			{
				return true; // early exit because nothing to parse
			}
			else if (!RHScount)
			{
				globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Production incomplete: %s", ruleString.c_str());
				return false; // fail because production is incomplete
			}
			processingNow = false; // end while-loop
			continue;
		}
		// ignore separators
		else if ((tok_iter->at(0) == ' ') || (tok_iter->at(0) == ',') || (tok_iter->at(0) == '\t'))
		{
			++tok_iter;
			continue;
		}
		// ignore parameters
		else if (tok_iter->at(0) == '@')
		{
			if (symbolCount)
			{
				globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Invalid  \"@\": %s", ruleString.c_str());
				return false; // fail because parameter definition inside production
			}
			else
			{
				return true; // early exit because it's a parameter
			}
		}
		// symbol is nonterminal
		else if (tok_iter->at(0) == '<')
		{
			ntSymbol = true;
			++tok_iter;
		}
		// process comments
		else if (tok_iter->at(0) == '%')
		{
			string comment;
			while (++tok_iter != tokens.end())
			{
				comment.append(*tok_iter);
			}
			globalMessageLog.print(COGMT_INFO_IO, VERBOSITY_MAX, "Grammar file comment: %s", comment.c_str());
		}
		else
		{
			symbolCount++;
			CogSymbol tempSymbol;
			if (ntSymbol)
			{
				CogProductionPool* pTempPool = NULL;
				bool isTemplate = true;
				do
				{
					// store token since we will need to peek at the next one
					string stok = *tok_iter;
					// next token decides what action to take - if token is a bracket, previous token was a pool identifier
					if ((++tok_iter != tokens.end()) && ((tok_iter->at(0) == '<') || (tok_iter->at(0) == '|')))
					{
						symbolCount++;
						
						if (!(tempSymbol.pPool = pGram->getPool(stok, pTempPool)))
						{
							// new pool
							tempSymbol.pPool = pGram->addPool(stok, pTempPool);
						}
						pTempPool = tempSymbol.pPool;
						if (tok_iter->at(0) == '|')
						{
							isTemplate = false;
						}
					}
					else
					{
						if (isTemplate) // (ID less)
						{
							// need to put it into a pool
							if (!(tempSymbol.pPool = pGram->getPool(stok, pTempPool)))
							{
								tempSymbol.pPool = pGram->addPool(stok, pTempPool);
							}
							tempSymbol.flag |= COGSF_TEMPLATE;
						}
						else // an instance (must have been preceeded by a pool identifier)
						{
							if (!(tempSymbol.id = pGram->getId(stok, pTempPool)))
							{
								tempSymbol.id = pGram->addId(stok, pTempPool);
							}
						}
						if ((tok_iter != tokens.end()) && (tok_iter->at(0) == '>'))
						{
							ntSymbol = false;
						}
						else
						{
							globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Missing closing \">\": %s", ruleString.c_str());
							return false; // fail because of unbalanced <>'s
						}
					}
				} 
				while ((++tok_iter != tokens.end()) && ntSymbol);
			}
			else // terminal symbol
			{
				// store token since we will need to peek at the next one
				string stok = *tok_iter;
				// next token decides what action to take - if token is a bracket, previous token was a parameterized terminal
				if ((++tok_iter != tokens.end()) && (tok_iter->at(0) == '<'))
				{
					stok.append(" ");
					bool backslashFlag = false;
					while (++tok_iter != tokens.end())
					{
						if (tok_iter->at(0) == '\\')
						{
							if (!backslashFlag)
							{
								backslashFlag = true;
								continue;
							}
						}
						else if ((tok_iter->at(0) == '>') && !backslashFlag)
						{
							break;
						}
						backslashFlag = false;
						stok.append(*tok_iter);
					}
					if (tok_iter == tokens.end())
					{
						globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Incomplete parameter after terminal symbol \"%s\": %s", stok.c_str(), ruleString.c_str());
						return false; // fail
					}
					++tok_iter;
				}
				uint32_t tempId;
				if (pGram->getTerminalId(stok, tempId))
				{
					// assign found id number as symbol's id
					tempSymbol.id = tempId;
					// assign terminal state
					tempSymbol.flag |= COGSF_TERMINAL;
					// count terminals - used to determine "terminal" productions
					terminalCount++;
				}
				else
				{
					globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Terminal of name \"%s\" either unknown or conflicting definitions between objectives: %s", stok.c_str(), ruleString.c_str());
					return false; // fail
				}
			}
			if (onLHS) // process template repetition (LHS template only)
			{
				if ((tok_iter != tokens.end()) && (tok_iter->at(0) == '*'))
				{
					string ttok = *tok_iter;
					if (++tok_iter == tokens.end())
					{
						globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Repetition value expected after symbol \"%s\" followed by asterisk: %s", ttok.c_str(), ruleString.c_str());
						return false; // fail
					}
					else
					{
						int p = atoi(tok_iter->c_str());
						if (p > 0)
						{
							if (tempSymbol.flag & COGSF_TEMPLATE) // ignore for instances
							{
								usedBy = static_cast<uint32_t>(p);
							}
							tok_iter++;
						}
						else
						{
							globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Repetition value must be given in whole integer parts (value \"%s\" for symbol \"%s\" cannot be interpreted as such): %s", (tok_iter)->c_str(), ttok.c_str(), ruleString.c_str());
							return false; // fail
						}
					}
				}
				else
				if (tempSymbol.flag & COGSF_TEMPLATE)
				{
					usedBy = 1;
				}
			}
			while ((tok_iter != tokens.end()) && ((tok_iter->at(0) == ' ') || (tok_iter->at(0) == '\t') || (tok_iter->at(0) == '\n'))) 
			{
				// skip whitespace
				++tok_iter;
			}
			// first symbol is LHS symbol
			if (onLHS)
			{
				if (tok_iter == tokens.end())
				{
					globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Production incomplete, LHS not followed by \"::=\" or \"::-\" or \"::~\": %s", ruleString.c_str());
					return false; // fail
				}
				// ::+ denotes a starting production
				else if (*tok_iter == "::+")
				{
					tempSymbol.flag |= (COGSF_STARTING | COGSF_EVAL);
				}
				// ::= denotes fully evaluable production
				else if (*tok_iter == "::=")
				{
					tempSymbol.flag |= COGSF_EVAL;
				}
				// ::- denotes any other production
				else if (*tok_iter != "::-")
				{
					globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Production LHS followed by \"%s\", but should be \"::=\" or \"::-\" or \"::~\": %s", tok_iter->c_str(), ruleString.c_str());
					return false; // fail
				}
				lhs = tempSymbol;
				onLHS = false;
				++tok_iter;
				symbolCount++;
			}
			// if LHS already processed, count this symbol as part of RHS
			else
			{
				rhs.push_back(tempSymbol);
				RHScount++;
			}
		}
	} 
	while (processingNow);
	if (terminalCount == RHScount)
	{
		// RHS consists only of terminals
		lhs.flag |= COGSF_CONST;
		globalMessageLog.print(COGMT_INFO_IO, VERBOSITY_MAX, "Terminal production: %d:%d", lhs.pPool->getId(), lhs.id );
	}
	return true;
}

void CogProduction::toStream(ostream &out, const CogGrammar* pGram) const
{
	lhs.pPool->ancestryToStream(0, out);
	if (!(lhs.flag & COGSF_TEMPLATE))
	{
		out << "|" << lhs.pPool->getLabel(lhs.id);
	}
	out << ">";
	if (usedBy > 1)
	{
		out << "*" << usedBy;
	}
	if (lhs.flag & COGSF_STARTING)
	{
		out << " ::+ ";
	}
	else
	if (lhs.flag & COGSF_EVAL)
	{
		out << " ::= ";
	}
	else
	{
		out << " ::- ";
	}
	vector<CogSymbol>::const_iterator rhs_iter = rhs.begin();
	while (rhs_iter != rhs.end())
	{
		if (rhs_iter->flag & COGSF_TERMINAL)
		{
			string tlabel;
			if (pGram->getTerminalLabel(rhs_iter->id, tlabel))
			{
				for (size_t i = 0; i < tlabel.size(); i++)
				{
					if (tlabel[i] == ' ')
					{
						tlabel[i] = '<';
						tlabel.append(">");
						break;
					}
				}
				out << tlabel;
			}
			else
			{
				out << rhs_iter->pPool->getLabel(rhs_iter->id);
			}
		}
		else
		{
			rhs_iter->pPool->ancestryToStream(0, out);
			if (!(rhs_iter->flag & COGSF_TEMPLATE))
			{
				out << "|" << rhs_iter->pPool->getLabel(rhs_iter->id);
			}
			out << ">";
		}
		++rhs_iter;
		if (rhs_iter != rhs.end())
		{
			out << " ";
		}
		else
		{
			out << "\n";
		}
	}
}

void CogProduction::expandToStream(ostream &out, const CogGrammar* pGram, bool printHeader) const
{
	if (!(lhs.flag & COGSF_TEMPLATE)) // instances only
	{
		if (printHeader)
		{
			lhs.pPool->ancestryToStream(0, out);
			out << "|" << lhs.pPool->getLabel(lhs.id) << ">";
			if (lhs.flag & COGSF_STARTING)
			{
				out << " ::+ ";
			}
			else
			if (lhs.flag & COGSF_EVAL)
			{
				out << " ::= ";
			}
			else
			{
				out << " ::- ";
			}
		}
		else 
		if (lhs.flag & COGSF_EVAL)
		{
			out << "(";
		}
		vector<CogSymbol>::const_iterator rhs_iter = rhs.begin();
		while (rhs_iter != rhs.end())
		{
			if (rhs_iter->flag & COGSF_TERMINAL)
			{
				string tlabel;
				if (pGram->getTerminalLabel(rhs_iter->id, tlabel))
				{
					for (size_t i = 0; i < tlabel.size(); i++)
					{
						if (tlabel[i] == ' ')
						{
							tlabel[i] = '<';
							tlabel.append(">");
							break;
						}
					}
					out << tlabel;
				}
				else
				{
					out << rhs_iter->pPool->getLabel(rhs_iter->id);
				}
			}
			else
			{
				if (rhs_iter->pRule)
				{
					rhs_iter->pRule->expandToStream(out, pGram, false);
				}
			}
			++rhs_iter;
			if (rhs_iter != rhs.end())
			{
				out << " ";
			}
		}
		if (printHeader)
		{
			out << "\n";
		}
		else
		if (lhs.flag & COGSF_EVAL)
		{
			out << ")";
		}
	}
}

// ------------------------------------------------------------------------------------------